package cn.datawin.dockerclient.docker;

import cn.datawin.bms.deploy.entity.DeplBranchInfoEntity;
import cn.datawin.bms.deploy.entity.DeplDockerInfoEntity;
import cn.datawin.constant.DOCKER;
import cn.datawin.dockerclient.service.DeplDockerInfoService;
import cn.datawin.dockerclient.service.DeplServerModuleInfoService;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.InspectContainerResponse;
import com.github.dockerjava.api.command.PullImageResultCallback;
import com.github.dockerjava.api.model.*;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
import com.github.dockerjava.transport.DockerHttpClient;
import com.github.dockerjava.transport.SSLConfig;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Slf4j
@Service
@RequiredArgsConstructor
public class DockerDeployService {


	private final DeplDockerInfoService deplDockerInfoService;
	private final DeplServerModuleInfoService deplServerModuleInfoService;
	private final Environment env;
	public boolean check(String container_name, String server_ip) {
		long count = deplDockerInfoService.countByContainerName(container_name, server_ip);
		return count == 0;
	}
	private DockerClient getDockerClient(String server_ip) {
		DockerClient dockerClient;
		String[] activeProfiles = env.getActiveProfiles();
		String activeProfile = activeProfiles[0];
		if (activeProfile.equals("dev")) {
			dockerClient = this.getDockerClientDev(server_ip);
		}else{
			dockerClient = this.getDockerClientProd(server_ip);
		}
		return dockerClient;
	}


	public DockerClient getDockerClientDev(String serverIp) {
		// 配置 Docker 客户端
		DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
//                .withDockerHost("unix:///var/run/docker.sock") // 对于 Unix 套接字
				// 或对于 TCP 连接
//				.withDockerHost("tcp://localhost:2375")
				.withDockerHost("tcp://" + serverIp + ":" + DOCKER.REMOTE_PORT)
				.withDockerTlsVerify(false)
				.build();
		// 在getDockerClient方法中添加：
		DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
				.dockerHost(config.getDockerHost())
				.maxConnections(100)
				.connectionTimeout(Duration.ofSeconds(30))
				.responseTimeout(Duration.ofSeconds(45))
				.build();

		return DockerClientImpl.getInstance(config, httpClient);
	}


	public DockerClient getDockerClientProd(String serverIp) {
		// 配置 Docker 客户端
		String certPath = "/mydata/logs/.docker/" + serverIp.replaceAll("\\.", "_");
		DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
//                .withDockerHost("unix:///var/run/docker.sock") // 对于 Unix 套接字
				// 或对于 TCP 连接
//				.withDockerHost("tcp://localhost:2375")
				.withDockerHost("tcp://" + serverIp + ":" + DOCKER.REMOTE_PORT)
				.withDockerTlsVerify(true)
				.withDockerCertPath(certPath)
				.build();
		// 2. 创建 DockerClient（自动加载证书）
		SSLConfig sslConfig = new SecureDockerSSLConfig(
				certPath,
				"datawin1q2w3e4r" // 从安全配置读取
		);
		// 在getDockerClient方法中添加：
		DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
				.dockerHost(config.getDockerHost())
				.sslConfig(sslConfig)  // 使用自定义 SSL 配置
				.maxConnections(100)
				.connectionTimeout(Duration.ofSeconds(30))
				.responseTimeout(Duration.ofSeconds(45))
				.build();

		return DockerClientImpl.getInstance(config, httpClient);
	}



//	@PostConstruct
//	public void test()  {
//		try {
//			String branch = "1.0.0";
//			String module = "voice";
//			String server_ip = "172.25.2.33";
//			//ImageName
//			String dockerImageName = DOCKER.REGISTRY_URL + "/" + module + ":" + branch;
//			log.info("镜像名为：" + dockerImageName);
//			//docker pull code.data-win.cn:10003/shengdong/shengdong-cloud-voice-server:1.0.0
//			//需要发布的服务器
//			DockerClient dockerClient = this.getDockerClientProd(server_ip);
//
//			System.out.println("dockerClient = " + dockerClient);
//
//			pullImage(dockerClient, dockerImageName);
//		} catch (Exception e) {
//			log.error("拉取镜像失败", e);
//		}
//
//	}

	public String active(String module, DeplBranchInfoEntity branchInfo, String container_name, String config_json, String server_ip, String server_port) throws Exception {
		String branch = branchInfo.getName();
		//ImageName
		String dockerImageName = DOCKER.REGISTRY_URL + "/" + module + ":" + branch;
		log.info("镜像名为：" + dockerImageName);
		//docker pull code.data-win.cn:10003/shengdong/shengdong-cloud-voice-server:1.0.0
		//需要发布的服务器
		DockerClient dockerClient = getDockerClient(server_ip);

		// 拉取镜像
//		pullImage(dockerClient, dockerImageName);
		// 构建参数
		List<String> envs = buildArgs(config_json, module, server_ip, server_port, branchInfo);
		// 运行容器
		return runContainer(dockerClient, dockerImageName, container_name, server_port, config_json,envs);
	}
	// 拉取镜像
	private void pullImage(DockerClient dockerClient, String imageName) throws InterruptedException {
		log.info("开始拉取镜像: {}", imageName);
		dockerClient.pullImageCmd(imageName)
				.withAuthConfig(dockerClient.authConfig()
						.withUsername(DOCKER.REGISTRY_USER)
						.withPassword(DOCKER.RESITRY_PWD))
				.exec(new PullImageResultCallback() {
					@Override
					public void onNext(PullResponseItem item) {
						String status = item.getStatus();
						ResponseItem.ProgressDetail pd = item.getProgressDetail();
						if (status != null && pd != null) {
							log.info("镜像拉取进度: {}--{}",pd.getCurrent()+"/"+ pd.getTotal(),status  );
						}
						super.onNext(item);
					}
				})
				.awaitCompletion();
		log.info("镜像拉取完成: {}", imageName);
	}


	private List<String> buildArgs(String config_json, String module, String ip, String port, DeplBranchInfoEntity branchInfo){
		List<String> args = new ArrayList<>();
		JSONObject configJson = new JSONObject();
		if(StrUtil.isNotBlank(config_json)){
			configJson= JSONObject.parseObject(config_json);
			Set<String> keys = configJson.keySet();
			for (String key : keys) {
				args.add(key + "=" + configJson.getString(key));
			}
		}
//		//添加内存限制
//		if(module.equals(MODULE_TYPE.RULE)){
//			args.add("JAVA_OPTS=-Xmx4096m");
//		}else{
//			args.add("JAVA_OPTS=-Xmx2048m");
//		}
		//如果需要注册到nacos注册中心，需要在config_json中增加一个属性NEED_REG，值为1
		if(config_json.contains("NEED_REG")){
			args.add("NACOS_REG_IP="+ip);
		}


		args.add("SERVER_PORT="+port);
		//添加nacos配置中心workspace id。若未配置则不添加，容器在启动时因拉取不到配置报错。
		String nacos_ip = branchInfo.getNacosIp();
		String nacos_id = branchInfo.getNacosId();
		if(StrUtil.isNotBlank(nacos_ip)){
			args.add("NACOS_HOST="+nacos_ip);
		}
		if(StrUtil.isNotBlank(nacos_id)){
			args.add("NACOS_NAMESPACE_ID=" + nacos_id);
		}
		return args;
	}


	private String runContainer(DockerClient dockerClient, String imageName, String containerName, String serverPort, String configJson, List<String> envs) {
		List<ExposedPort> exposedPorts = new ArrayList<>();
		List<PortBinding> portBindings = new ArrayList<>();
		//如果是cmpp-server，外部tcp端口映射内部的7891端口，cmpp-server默认监听7891端口
		if(imageName.contains("cmpp-server")){
			exposedPorts.add(ExposedPort.tcp(DOCKER.CMPP_DEFAULT_PORT));
			portBindings.add(new PortBinding( Ports.Binding.bindPort(DOCKER.CMPP_DEFAULT_PORT), ExposedPort.tcp(DOCKER.CMPP_DEFAULT_PORT) ));
		}
		//暴露对外的http端口，内外端口保持一致
		exposedPorts.add(ExposedPort.tcp(Integer.parseInt(serverPort)));
		portBindings.add(new PortBinding( Ports.Binding.bindPort(Integer.parseInt(serverPort)), ExposedPort.tcp(Integer.parseInt(serverPort))));
		HostConfig hostConfig = HostConfig.newHostConfig().withPortBindings(portBindings);
		//如果是voice模块，设置内存大小为3GB
		if(imageName.contains("voice")){
			hostConfig.withMemory(3L * 1024 * 1024 * 1024);
		}
		hostConfig.withBinds(Bind.parse("/mydata/logs:/mydata/logs"));
		CreateContainerResponse container = dockerClient.createContainerCmd(imageName)
				.withName(containerName)
				.withExposedPorts(exposedPorts)
				.withHostConfig(hostConfig)
				.withEnv(envs)
				.exec();

		dockerClient.startContainerCmd(container.getId()).exec();
		return container.getId();
	}



	public void stop(int dockerinfo_id) {

		DeplDockerInfoEntity dockerInfo = deplDockerInfoService.getById(dockerinfo_id);
		String containerId = dockerInfo.getContainerId();
		String server_ip = dockerInfo.getServerIp();
		DockerClient dockerClient = getDockerClient(server_ip);
		try {
			// 先获取容器状态
			InspectContainerResponse containerInfo = dockerClient.inspectContainerCmd(containerId).exec();
			if (Boolean.TRUE.equals(containerInfo.getState().getRunning())) {
				// 只有容器在运行状态时才停止
				dockerClient.stopContainerCmd(containerId).exec();
			}

		} catch (Exception e) {
			log.warn("停止容器时发生异常，容器可能不存在或已停止: {}", e.getMessage());
		}
		//更新容器状态为暂停
		deplDockerInfoService.updateStatusById(dockerinfo_id, DOCKER.STATUS.PAUSED);

	}

	public void restart(int dockerinfo_id) {

		DeplDockerInfoEntity dockerInfo = deplDockerInfoService.getById(dockerinfo_id);
		String containerId = dockerInfo.getContainerId();
		String server_ip = dockerInfo.getServerIp();
		DockerClient dockerClient = getDockerClient(server_ip);
		// 开启容器
		dockerClient.restartContainerCmd(containerId).exec();
		//更新容器状态为运行中
		deplDockerInfoService.updateStatusById(dockerinfo_id, DOCKER.STATUS.RUNNING);
	}


	public void remove(int dockerinfo_id) {

		DeplDockerInfoEntity dockerInfo = deplDockerInfoService.getById(dockerinfo_id);
		String containerId = dockerInfo.getContainerId();
		String server_ip = dockerInfo.getServerIp();
		DockerClient dockerClient = getDockerClient(server_ip);
		try {
			// 先停止容器（如果正在运行）
			this.stop(dockerinfo_id);
			// 删除容器
			dockerClient.removeContainerCmd(containerId).exec();

		} catch (Exception e) {
			e.printStackTrace();
			// 容器可能已经不存在或者停止
			log.warn("容器可能已经不存在或者停止--{}",dockerinfo_id);
		}
		//更新容器状态为已销毁
		deplDockerInfoService.updateStatusById(dockerinfo_id, DOCKER.STATUS.REMOVED);
		//释放serverModuleInfo中的服务器端口
		deplServerModuleInfoService.updateServerModuleInfo(dockerInfo.getModuleId(),dockerInfo.getServerIp(),dockerInfo.getOutServerPort(),0);
	}

}
